db = require "./db"
utils = require "./utils"
dayjs = require "dayjs"
_ = require "lodash"
Puppet = require "wechaty-puppet"
Wechaty = require "wechaty"

_.templateSettings.interpolate = /{{([\s\S]+?)}}/g
MessageType= Puppet.types.Message
FriendshipType= Puppet.types.Friendship
{ UrlLink,  MiniProgram } = Wechaty

{ FileBox } = require "file-box"
bot = null

# __qrcode = ""
module.exports =
    get: ->
        return bot
    getPayload: ->
        return bot.currentUser
    load: (instance) ->
        bot = instance
        global["bot"] = bot
        bot.on "scan", (qrcode, status) ->
            bot.qrcode = qrcode
        bot.on "login", (user) ->
            await db.BOT.findOneAndUpdate { id: user.id }, { $set: user.payload }, upsert: true

            # bot.say "登陆成功 " + dayjs().format "MM-DD HH:mm:ss"

        bot.on "logout", (user) ->
            console.log "logout"
            console.log user

        bot.on "message", (message) ->
            await onMessage message

        bot.on "friendship", (friendship) ->
            await onFriendShip friendship

        bot.on "room-join", (room, inviteeList, inviter) ->
            console.log(arguments)
            await onRoomJoin room, inviteeList, inviter

        bot.on "room-leave", (room, leaverList, remover) ->
            console.log remover

        bot.on "room-topic", (room, topic, oldTopic, changer) ->
            await updateRoom room

        bot.on "room-invite", (roomInvitation) ->
            onRoomInvite roomInvitation
        console.log 'start bot'
        await bot.start()

onFriendShip = (fs) ->
    switch fs.type()
        when FriendshipType.Receive
            try
                await fs.accept()
            catch err
                contact = fs.contact()
                await updateContact contact
                await sendWelcome contact
        when FriendshipType.Confirm
            contact = fs.contact()

            await updateContact contact
            await sendWelcome contact
        else
            console.log "unknow friendship type" + fs.type()

onRoomInvite = (roomInvitation) ->
    await roomInvitation.accept()

onRoomJoin = (room, inviteeList, inviter) ->
    # 无邀请人结束统计
    console.log "room join " + (await room.topic())

    # 更新群信息
    roomPayload = room.payload
    console.log  roomPayload
    roomPayload.botid = bot.currentUser?.id

    # if inviter
    #   await updateRoomMember inviter, room



    if inviteeList.length == 0
        r = await db.ROOM.findOne
            id: room.id
        inviteeListID = _.difference(roomPayload.memberIdList, r.memberIdList)
        console.log "diff ids"
        console.log roomPayload.memberIdList
        console.log r.memberIdList
        console.log inviteeListID
        for i in inviteeListID
            console.log i
            try
                c = await bot.CONTACT.load i
                console.log c
                if c
                    inviteeList.push c
                else
                    inviteeList.push {id: i}
            catch
                inviteeList.push {id: i}

    await updateRoom room
    await sendRoomWelcome room, inviteeList

    # 机器人被邀请, 发送管理页面
    for invitee in inviteeList
        if invitee.payload and invitee.self()
            await db.ROOM.findOneAndUpdate
                id: room.id
            ,
                $set:
                    inviterid: inviter.id
            try
                await syncRoomMembers room
            catch err
                console.log err
            await sendRoomInviteWelcome inviter, room
            await sendClientHome inviter
    if inviter
        payload = inviter.payload
        payload.roomid = room.id
        await db.ROOM_CONTACT.findOneAndUpdate
            id: inviter.payload.id
            botid: bot.currentUser.id
            roomid: room.id
        ,
            $set: payload
        ,
            upsert: true

        inviterContact =
            await db.ROOM_CONTACT.findOneAndUpdate
                id: inviter.payload.id
                botid: bot.currentUser.id
                roomid: room.id
            ,
                $inc: invite: inviteeList.length

        incPoint = await invitePointHandler room, inviteeList, inviter

    for invitee in inviteeList
        await updateRoomMember invitee, room

checkRoomUsable = (room) ->
    r = await db.ROOM.findOne id: room.id
    if r?.expires < new Date() and r.inviterid
        # contact = await bot.Contact.load r.inviterid
        # await room.say "本群积分功能已过期, 点我头像进入私聊的管理页面开通", contact
        return false
    return true

invitePointHandler = (room, inviteeList, inviter) ->
    roomData = await db.ROOM.findOne id: room.id
    client = await db.CLIENT.findOne contactid: roomData.inviterid
    return 0 unless client
    clientid = client._id

    incPoint = 0
    inviteLength = 0
    rule =
        await db.POINT_RULE.findOne
            clientid: clientid
            roomid: room.id
    console.log rule

    return 0 unless rule and rule.invite_point > 0 and rule.invite_status

    console.log "invite stat"

    hasExists = false
    for invitee in inviteeList
        # 判断重复邀请
        exists =
            await db.ROOM_INVITE.count
                inviteeid: invitee.id
                clientid: client._id
                inviterid: inviter.id

        if exists
            hasExists = invitee.id
            continue
            # 插入邀请关系
        await db.ROOM_INVITE.insert
            inviteeid: invitee.id
            inviterid: inviter.id
            roomid: room.id
            clientid: client._id

        incPoint += rule.invite_point * 1
        inviteLength++

        # 增加积分
        await db.POINT_DATA.findOneAndUpdate
            clientid: clientid
            contactid: inviter.id
        ,
            $inc:
                point: rule.invite_point * 1
                totalPont: rule.invite_point * 1
        ,
            upsert: true

        # 插入积分记录
        await db.POINT_LOG.insert
            clientid: clientid
            roomid: room.id
            contactid: inviter.id
            inviterid: inviter.id
            inviteeid: invitee.id
            point: rule.invite_point * 1
            desc: "邀请入群获得"

    pointData =
        await db.POINT_DATA.findOne
            clientid: clientid
            contactid: inviter.id

    return unless rule.invite_notify

    if hasExists
        room.say "您的好友 已经重复入群啦，请勿重复邀请！", inviter
    if inviteLength > 0
        url = await getMallUrl clientid
        setting = await db.getMainSetting clientid
        msg =
            _.template(setting.invitetips)(
                邀请积分: incPoint
                现有积分: pointData.point
                兑换地址: url
            )
        room.say msg, inviter

getRoomClient = (db_room) ->
    await db.CLIENT.findOne contactid: db_room.inviterid

getRoomRule = (room) ->
    r = await db.ROOM.findOne id: room.id
    return unless r
    client = await db.CLIENT.findOne contactid: r.inviterid
    return unless client
    rule = await db.POINT_RULE.findOne clientid: client._id, roomid: r.id
    return rule

onMessage = (msg) ->
    text = msg.text()
    room = null
    # console.log text
    try
        room = msg.room()
    catch err
        console.log err

    # 收到超过两小时消息忽略处理
    return if msg.age() > 60 * 60 * 2

    # contactList = await msg.mentionList()

    # console.log contactList

    if room
        return unless await checkRoomUsable room
        await updateRoomMember msg.talker(), room
    else
        await updateContact msg.talker()

    if text is "欢迎语" and not room
        return await sendWelcome msg.talker()

    if text is "欢迎语" and room
        return await sendRoomWelcome room

    if text is "测试链接信息"
        return await msg.say(
            new UrlLink
                description: "不合时宜"
                thumbnailUrl: ""
                title: "女性折叠：同一个世界，不同的梦想"
                url:
                    "https://www.xiaoyuzhoufm.com/episode/620dcfceeac7b403d7e14511?s=eyJ1IjogIjYwNmI5ZjAxZTBmNWU3MjNiYjgxYTRlMSJ9"
        )

    if text is "邀请查询" and room
        return await keywordInviteReply msg
    if text is "邀请统计" and room
        return await keywordInviteReply2 msg
    if text is "积分查询" and room
        return await keywordPointReply msg

    if text is "兑换" and room
        return await keywordMallReply msg
    if text is "积分兑换" and room
        return await keywordMallReply msg
    if text is "积分商城" and room
        return await keywordMallReply msg
    if text is "排行榜" and room
        return await keywordRankPageReply msg
    # if text is "邀请排行" and room
    #     return await keywordRankReply msg
    if text is "我的后台" and !room
        return await sendClientHome msg.talker()
    if text is "后台地址" and !room
        return await sendClientHome msg.talker()

    if text is "同步成员数据" and room
        await syncRoomMembers room
        return room.say "同步成功"

    if text.startsWith("zp") and room
        return await bindContact msg

    if room and /[-+加减]\s?\d+$/.test text
        return await setRoomPoint msg
    if text and room
        await keywordRely msg

    mentionSelf = await msg.mentionSelf()
    mentionList = await msg.mentionList()
    if mentionSelf and mentionList.length is 1 and room
        return await sayRoomHelp msg

    if !room
        setting = await db.getMainSetting()
        if setting.keyrooms
            for line in setting.keyrooms.split '\n'
                words = line.split ' '
                if words[0] and words[0] is text
                    msg.talker().say("正在拉你入群, 请稍等")
                    return await bot.Room.load(words[1]).add msg.talker()
        return await saveMsg msg

setRoomPoint = (msg) ->
    console.log "set point"
    contactList = await msg.mentionList()
    text = msg.text()
    text = text.replace(/加/g, "+").replace /减/g, "-"
    room = msg.room()
    db_room = await db.ROOM.findOne id: room.id
    client = await getRoomClient db_room
    talker = msg.talker()
    unless db_room.inviterid is talker.id
        return

    if contactList.length > 1
        return
    for contact in contactList
        # console.log contact
        contactid = contact.id
        clientid = client._id

        inputPoint = parseInt /[-+]\s?\d+\s?$/.exec text

        if isNaN inputPoint
            return await room.say "输入数字有误"

        pd =
            await db.POINT_DATA.findOneAndUpdate
                clientid: clientid
                contactid: contactid
            ,
                $inc:
                    point: inputPoint
            ,
                upsert: true

        await db.POINT_LOG.insert
            contactid: contactid
            clientid: clientid
            point: inputPoint
            desc: "管理员指令修改"
        url = await getMallUrl clientid
    await room.say "修改积分成功， 积分" + (if inputPoint>0 then '增加' else '扣减') + inputPoint + "\n 积分可以在此处兑换奖励： #{url}", contactList...

sayRoomHelp = (msg) ->
    room = msg.room()
    db_room = await db.ROOM.findOne id: room.id

    unless db_room and db_room.inviterid
        await updateRoom room
        await db.ROOM.findOneAndUpdate { id: room.id }, $set: inviterid: msg.talker().id
        return await room.say "是管理者", msg.talker()

    contact = await bot.Contact.load db_room.inviterid
    rule = await getRoomRule room
    signtmpl = ""
    signwordtmpl = ""
    invitetmpl = ""
    if rule and rule.sign_status
        signtmpl = "#{rule.sign_word or "签到"} #{rule.sign_point}积分"
        signwordtmpl = "【#{rule.sign_word or "签到"}】- 每日打卡签到积分"
    if rule and rule.invite_status
        invitetmpl = "邀请入群 #{rule.invite_point}积分"
    content = """
    指令一览
    #{signwordtmpl}
    【积分查询】 - 查看我的积分
    【邀请查询】 - 查看我的邀请人数
    【积分兑换】 - 查看积分兑换入口
    【排行榜】 - 查看积分榜
    积分规则
    #{invitetmpl}
    #{signtmpl}
    管理负责人 #{contact.payload?.name}
    """
    await room.say content

sendRoomWelcome = (room, inviteeList) ->
    await utils.rsleep()
    try
        rule = await getRoomRule room
        return unless rule
        if rule.welcome
            if inviteeList
                await room.say rule.welcome
            else
                await room.say rule.welcome, ...inviteeList
    catch err
        console.log err

keywordRely = (msg) ->
    talker = msg.talker()
    room = msg.room()
    text = msg.text()
    payload = talker.payload
    db_room = await db.ROOM.findOne id: room.id
    return unless db_room
    client = await getRoomClient db_room
    return unless client
    rule = await db.POINT_RULE.findOne roomid: room.id, clientid: client._id

    return unless rule
    if rule.replys
        for reply in rule.replys
            if reply.keyword?.split(' ')?.includes(text)
                for m in reply.msgs
                    await sendRoomMsg room, m._id


    unless rule?.sign_status
        console.log room.id + " no sign rule setted"
        return

    signword = rule.sign_word or "签到"

    if signword is text
        exists =
            await db.POINT_LOG.findOne
                contactid: payload.id
                type: "sign"
                # roomid: room.id
                clientid: client._id
                date: dayjs().format "YYYY-MM-DD"
        if rule.sign_start and rule.sign_start > dayjs().format "HH:mm"
            return await room.say "未到签到时间 #{rule.sign_start} - #{rule.sign_end}", talker

        if rule.sign_end and rule.sign_end < dayjs().format "HH:mm"
            return await room.say "超过签到时间 #{rule.sign_start} - #{rule.sign_end}", talker

        if exists
            return await room.say "今天已签到， 请勿重复操作。", talker

        keepMonth = await db.POINT_LOG.count
            contactid: payload.id
            type: "sign"
            clientid: client._id
            date: new RegExp(dayjs().format("YYYY-MM"))

        lastDate = await db.POINT_LOG.count
            contactid: payload.id
            type: "sign"
            clientid: client._id
            date: dayjs().add(-1, 'days').format("YYYY-MM-DD")
        pointData = ""
        unless lastDate
            pointData = await db.POINT_DATA.findOneAndUpdate
                contactid: talker.id
                clientid: client._id
            ,
                $set:
                    keepdays: 0
            ,
                upsert: true

        await db.POINT_LOG.insert
            contactid: payload.id
            type: "sign"
            date: dayjs().format "YYYY-MM-DD"
            clientid: client._id
            roomid: room.id
            point: rule.sign_point
            desc: "群签到获得"

        pointData =
            await db.POINT_DATA.findOneAndUpdate
                contactid: talker.id
                clientid: client._id
            ,
                $inc:
                    point: rule.sign_point * 1
                    totalPont: rule.sign_point * 1
                    keepdays: 1
            ,
                upsert: true


        url = await getMallUrl client._id
        setting = await db.getMainSetting client._id
        msg =
            _.template(setting.signtips)(
                签到积分: rule.sign_point
                现有积分: pointData.point
                兑换地址: url
            )

        msg += "\n本月累计签到#{keepMonth+1}次, 连续签到#{pointData.keepdays}天"



        if rule?.sign_keepdays <= pointData.keepdays
            msg += "获得额外积分 #{rule.sign_keeppoints}"
            await db.POINT_LOG.insert
                contactid: payload.id
                type: "sign"
                date: dayjs().format "YYYY-MM-DD"
                clientid: client._id
                roomid: room.id
                point: rule.sign_keeppoints
                desc: "群累计签到获得"
            pointData = await db.POINT_DATA.findOneAndUpdate
                contactid: talker.id
                clientid: client._id
            ,
                $set:
                    keepdays: 0
                $inc:
                    point: rule.sign_keeppoints * 1
                    totalPont: rule.sign_keeppoints * 1

        if rule.sign_notify
            await room.say msg, talker

keywordRankReply = (msg) ->
    room = msg.room()
    room.say "排行榜在哪里"

keywordPointReply = (msg) ->
    room = msg.room()
    db_room = await db.ROOM.findOne id: room.id
    return unless db_room
    client = await getRoomClient db_room
    return unless client
    talker = msg.talker()

    pointData =
        await db.POINT_DATA.findOne
            clientid: client._id
            contactid: talker.id
    await room.say "现有#{pointData?.point or 0}积分", talker

keywordMallReply = (msg) ->
    room = msg.room()
    db_room = await db.ROOM.findOne id: room.id
    return unless db_room
    client = await getRoomClient db_room
    return unless client

    url = await getMallUrl client._id
    await room.say "兑换地址 #{url}"

keywordRankPageReply= (msg) ->
    room = msg.room()
    db_room = await db.ROOM.findOne id: room.id
    return unless db_room
    client = await getRoomClient db_room
    return unless client

    url = await utils.shortlink process.env.DOMAIN + "/user/client/" + client._id + '/page/rank'
    await room.say "排行榜 #{url}"

getMallUrl = (clientid) ->
    return await utils.shortlink process.env.DOMAIN + "/user/client/" + clientid

# keywordRankReply = (msg) ->
# 	await msg.room().say "功能维护中"

keywordInviteReply = (msg) ->
    roomContact =
        await db.ROOM_CONTACT.findOne
            id: msg.talker().id
            roomid: msg.room().id
    invite = roomContact?.invite or 0

    # db_room = await db.ROOM.findOne id: msg.room().id
    # client = await getRoomClient db_room
    # console.log client
    # console.log msg.talker().id
    # invite = await db.ROOM_INVITE.count
    #     clientid: client._id
    #     inviterid: msg.talker().id
    await msg.room().say "系统邀请人数" + invite, msg.talker()


keywordInviteReply2 = (msg) ->
    roomContact =
        await db.ROOM_CONTACT.findOne
            id: msg.talker().id
            roomid: msg.room().id
    # invite = roomContact?.invite or 0

    db_room = await db.ROOM.findOne id: msg.room().id
    client = await getRoomClient db_room
    console.log client
    console.log msg.talker().id
    invite = await db.ROOM_INVITE.count
        clientid: client._id
        inviterid: msg.talker().id
    await msg.room().say "邀请人数" + invite, msg.talker()


updateRoom = (room) ->
    payload = room.payload
    payload.topic = await room.topic()
    console.log payload
    r =
        await db.ROOM.findOneAndUpdate
            id: room.id
        ,
            $set: payload
        ,
            upsert: true
    unless r.expires
        await db.ROOM.findOneAndUpdate
            _id: r._id
        ,
            $set:
                expires:
                    dayjs r._id.getTimestamp()
                        .add 10, "minute"
                        .toDate()

updateContact = (contact) ->
    try
        await db.CONTACT.findOneAndUpdate
            id: contact.id
            botid: bot.currentUser.id
        ,
            $set: contact.payload
        ,
            upsert: true

        user =
            # headimgurl: contact.payload.avatar
            nickname: contact.payload.name
            contactid: contact.id
        id = await utils.getUserId user
        await db.USER.findOneAndUpdate { id: id }, { $set: user }, upsert: true
    catch err
        console.log err
        contact.sync()

sendWelcome = (contact) ->
    if contact.payload.corporation and not contact.payload.corporation.includes "微信"
        return await contact.say "暂时不支持企业微信账号, 请到个人微信中加我, 并拉我入企业群"
    setting = await db.getMainSetting()
    await contact.say setting.welcometips

    await utils.rsleep()
    console.log setting.welcomeimage
    console.log contact
    if setting.welcomeimage
        fileBox = await FileBox.fromUrl setting.welcomeimage, 'welcome.png'
        await contact.say fileBox

sendRoomInviteWelcome = (inviter, room) ->
    try
        await utils.rsleep()
        await updateContact inviter

        contact = await bot.Contact.load inviter.id
        # if contact.payload.corporation and not contact.payload.corporation.includes "微信"
        #     return await contact.say "暂时不支持企业微信邀请人"
    catch err
        console.log err
    url = await utils.shortlink process.env.DOMAIN + "/client/bind/" + inviter.id
    topic = await room.topic()
    msg = """
	我已经成功进入群聊：【#{topic}】

	👉点击下方链接配置您的群规则：
	#{url}

	"""
    await contact.say msg
    setting = await db.getMainSetting()
    await room.say setting.joinroomtips

sendClientHome = (contact) ->
    await utils.rsleep()
    contact = await bot.Contact.load contact.id
    console.log contact
    # if contact.payload.corporation and not contact.payload.corporation.includes "微信"
    #     return await contact.say "暂时不支持企业微信邀请人"
    await updateContact contact

    if contact.id
        url = await utils.shortlink process.env.DOMAIN + "/client/bind/" + contact.id
        console.log url
        await contact.say "您的管理后台：" + url
    else
        await contact.say "获取后台地址失败"

syncRoomMembers = (room) ->
    members = await room.memberAll()
    for member in members
        await updateRoomMember member, room

updateRoomMember = (member, room) ->
    try
        payload = member.payload
        return unless payload
        payload.roomid = room.id
        await db.ROOM_CONTACT.findOneAndUpdate
            id: member.payload.id
            botid: bot.currentUser.id
        ,
            $set: member.payload
        ,
            upsert: true
        user =
            # headimgurl: member.payload.avatar
            nickname: member.payload.name
            contactid: member.id
        id = await utils.getUserId user
        await db.USER.findOneAndUpdate { id: id }, { $set: user }, upsert: true
    catch err
        console.log err
        member.sync()

bindContact = (msg) ->
    text = msg.text()
    openid = text.replace /^zp/, ""
    contactid = msg.talker().id
    await updateContact msg.talker()
    # await db.USER.
    await db.USER.findOneAndUpdate
        contactid: contactid
        openid:
            $exists: false
    ,
        $set: openid: openid
    msg.room().say "绑定成功, 请重新进入兑换页面", msg.talker()

saveMsg = (msg) ->
    contact = msg.talker()
    inserted = null
    console.log msg
    console.log msg.type()
    client = await db.CLIENT.findOne contactid: contact.id
    unless client
        console.log "no client contactid " + contact.id
        return
    try
        if msg.type() is MessageType.MiniProgram
            mini = await msg.toMiniProgram()
            inserted =
                await db.MESSAGE.insert
                    contactid: contact.id
                    clientid: client._id
                    type: msg.type()
                    payload: mini.payload

        if msg.type() is MessageType.Channel
            channel = await msg.toChannel()
            console.log('channel')
            console.log(channel)
            inserted =
                await db.MESSAGE.insert
                    contactid: contact.id
                    clientid: client._id
                    type: msg.type()
                    payload: channel.payload

        if msg.type() is MessageType.Image


            img = await msg.toImage()
            await utils.rsleep(3000)
            hdFile = await img.hd()
            filebox = await msg.toFileBox()
            file64 = await filebox.toBase64()

            path = "#{process.cwd()}/upload/#{hdFile.name}"
            await hdFile.toFile("#{process.cwd()}/upload/#{hdFile.name}", true)

            inserted =
                await db.MESSAGE.insert
                    contactid: contact.id
                    clientid: client._id
                    type: msg.type()
                    payload: file64
                    path: path

        if msg.type() is MessageType.Url
            urllink = await msg.toUrlLink()
            # file64 = await filebox.toBase64()
            inserted =
                await db.MESSAGE.insert
                    contactid: contact.id
                    clientid: client._id
                    type: msg.type()
                    payload: urllink.payload

        # if msg.type() is MessageType.Attachment or msg.type() is MessageType.Video
        #   file = await msg.toFileBox()
        #   inserted =
        #     await db.MESSAGE.insert
        #       contactid: contact.id
        #       clientid: client._id
        #       type: msg.type()
        #       filebox: true
        #       payload: msg.toString()
        #       json: file.toJSON()

        if msg.type() is MessageType.Text
            inserted =
                await db.MESSAGE.insert
                    contactid: contact.id
                    clientid: client._id
                    type: msg.type()
                    payload: msg.text()

    catch e
        console.log e
    if inserted

        await utils.rsleep()
        contact = await bot.Contact.load contact.id
        if contact.payload.corporation and not contact.payload.corporation.includes "微信"
            return await contact.say "暂时不支持企业微信邀请人"
        await updateContact contact
        url = await utils.shortlink process.env.DOMAIN + "/client/bind/" + contact.id
        # console.log url
        # await contact.say "您的管理后台：" + url


        await contact.say "素材已保存, 可到群发界面选择了, " + "您的管理后台：" + url


sendRoomMsg = (room, msgid)=>
    message = await db.MESSAGE.findOne msgid
    try
        if message.type is MessageType.Text
            await room.say message.payload

        if message.type is MessageType.MiniProgram
            await room.say new bot.MiniProgram message.payload

        if message.type is MessageType.Image
            filebox = FileBox.fromFile message.path
            await room.say filebox

        if message.type is MessageType.Url
            urllink = new bot.UrlLink message.payload
            await room.say urllink
    catch err
        console.log err